package cn.iocoder.yudao.module.promotion.service.prizedraw;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.ListUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.date.LocalDateTimeUtil;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import cn.iocoder.yudao.framework.common.exception.ErrorCode;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.framework.common.util.spring.SpringUtils;
import cn.iocoder.yudao.module.member.dal.dataobject.user.MemberUserDO;
import cn.iocoder.yudao.module.member.dal.mysql.user.MemberUserMapper;
import cn.iocoder.yudao.module.promotion.controller.admin.prizedraw.vo.*;
import cn.iocoder.yudao.module.promotion.controller.app.prizedraw.vo.PrizeDrawDoingReqVO;
import cn.iocoder.yudao.module.promotion.dal.dataobject.prizedraw.PrizeDrawAwardsDO;
import cn.iocoder.yudao.module.promotion.dal.dataobject.prizedraw.PrizeDrawAwardsRecordDO;
import cn.iocoder.yudao.module.promotion.dal.dataobject.prizedraw.PrizeDrawDO;
import cn.iocoder.yudao.module.promotion.dal.dataobject.prizedraw.PrizeDrawLotteryDO;
import cn.iocoder.yudao.module.promotion.dal.mysql.prizedraw.PrizeDrawAwardsMapper;
import cn.iocoder.yudao.module.promotion.dal.mysql.prizedraw.PrizeDrawAwardsRecordMapper;
import cn.iocoder.yudao.module.promotion.dal.mysql.prizedraw.PrizeDrawLotteryMapper;
import cn.iocoder.yudao.module.promotion.dal.mysql.prizedraw.PrizeDrawMapper;
import cn.iocoder.yudao.module.promotion.enums.prizedraw.PrizeDrawAwardTypeEnum;
import cn.iocoder.yudao.module.promotion.enums.prizedraw.PrizeDrawLotteryStatusEnum;
import cn.iocoder.yudao.module.system.dal.redis.RedisKeyConstants;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.google.common.base.Stopwatch;
import com.thoughtworks.xstream.core.SecurityUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.time.DateUtils;
import org.apache.poi.ss.formula.functions.T;
import org.redisson.api.RBucket;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;
import sun.security.util.SecurityConstants;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.time.temporal.ChronoUnit;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.module.promotion.enums.ErrorCodeConstants.*;

/**
 * 抽奖活动 Service 实现类
 *
 * @author 芋道源码
 */
@Slf4j
@Service
@Validated
public class PrizeDrawServiceImpl implements PrizeDrawService {

    @Resource
    private PrizeDrawMapper prizeDrawMapper;

    @Resource
    private MemberUserMapper memberUserMapper;

    @Resource
    private PrizeDrawAwardsMapper prizeDrawAwardsMapper;

    @Resource
    private PrizeDrawAwardsRecordMapper prizeDrawAwardsRecordMapper;

    @Resource
    private PrizeDrawLotteryMapper prizeDrawLotteryMapper;

    @Resource
    private RedissonClient redissonClient;

    @Resource
    private PrizeDrawAwardsService prizeDrawAwardsService;

    @Resource
    private PrizeDrawNumberMemberService prizeDrawNumberMemberService;

    @Resource
    private ThreadPoolTaskExecutor taskExecutor;

    private static final Integer MAXSOPE = 100000000;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Long createPrizeDraw(PrizeDrawSaveReqVO createReqVO) {

        Long intersection = prizeDrawMapper.intersection(createReqVO);
        if (intersection > 0) {
            throw exception(PRIZE_DRAW_THE_LOTTERY_ACTIVITY_TIME_DOES_NOT_ALLOW_OVERLAPPING);
        }

        PrizeDrawDO prizeDraw = BeanUtils.toBean(createReqVO, PrizeDrawDO.class);
        prizeDrawMapper.insert(prizeDraw);
        // 新增修改商品信息，刷新概率信息
        addPrizeDrawAwardsAndRefresh(createReqVO, prizeDraw.getPrizeDrawId());
        return null;
    }

    /**
     * 新增修改商品信息，刷新概率信息
     *
     * @param createReqVO
     * @param prizeDrawId
     */
    private void addPrizeDrawAwardsAndRefresh(PrizeDrawSaveReqVO createReqVO, Long prizeDrawId) {
        BigDecimal hundred = new BigDecimal("100");

        List<PrizeDrawAwardsSaveReqVO> awards = createReqVO.getAwards();
        if (CollUtil.isNotEmpty(awards)) {

            int sort = 1;
            for (PrizeDrawAwardsSaveReqVO award : awards) {
                award.setDrawSort(sort++);

                // 这里要验证概率，并计算出安慰奖的概率
                if (Integer.valueOf(1).equals(award.getDrawType())) {
                    hundred = NumberUtil.sub(hundred, award.getPrizeChance());
                    if (hundred.compareTo(BigDecimal.ZERO) < 0) {
                        throw exception(PRIZE_DRAW_PROBABILITY_SETTING_ERROR);
                    }

                    PrizeDrawAwardsDO prizeDrawAwards = BeanUtil.copyProperties(award, PrizeDrawAwardsDO.class);
                    prizeDrawAwards.setPrizeDrawId(prizeDrawId);
                    prizeDrawAwardsMapper.insert(prizeDrawAwards);
                }
            }

            PrizeDrawAwardsSaveReqVO comfort = awards.stream().filter(e -> Integer.valueOf(2).equals(e.getDrawType())).findFirst().orElse(null);
            // 单独处理安慰奖
            PrizeDrawAwardsDO comfortAwards = BeanUtil.copyProperties(comfort, PrizeDrawAwardsDO.class);
            comfortAwards.setPrizeDrawId(prizeDrawId);
            comfortAwards.setPrizeChance(hundred);
            prizeDrawAwardsMapper.insert(comfortAwards);

            //修改完概率后，需要重新刷新redis信息
            refreshRedisMotoPrizeDrawVoByIds(ListUtil.of(prizeDrawId));
        }
    }

    @Override
    public void updatePrizeDraw(PrizeDrawSaveReqVO updateReqVO) {
        // 校验存在
        validatePrizeDrawExists(updateReqVO.getPrizeDrawId());

        Long intersection = prizeDrawMapper.intersection(updateReqVO);
        if (intersection > 0) {
            throw exception(PRIZE_DRAW_THE_LOTTERY_ACTIVITY_TIME_DOES_NOT_ALLOW_OVERLAPPING);
        }

        PrizeDrawDO prizeDraw = BeanUtils.toBean(updateReqVO, PrizeDrawDO.class);
        prizeDrawMapper.updateById(prizeDraw);

        prizeDrawAwardsMapper.delete(new LambdaQueryWrapper<PrizeDrawAwardsDO>().eq(PrizeDrawAwardsDO::getPrizeDrawId, updateReqVO.getPrizeDrawId()));

        // 新增修改商品信息，刷新概率信息
        addPrizeDrawAwardsAndRefresh(updateReqVO, prizeDraw.getPrizeDrawId());
    }

    /**
     * 刷新redis信息by 抽奖活动ids
     *
     * @param prizeDrawIds
     */
    private void refreshRedisMotoPrizeDrawVoByIds(List<Long> prizeDrawIds) {
        for (Long prizeDrawId : prizeDrawIds) {
            PrizeDrawRespVO todayMotoPrizeDraw = this.getTodayMotoPrizeDraw(prizeDrawId);
            //当活动存在redis时，才进行更新
            if (todayMotoPrizeDraw == null) {
                continue;
            }

            //将最新的 原始数据和当天的 实时活动数据刷新
            refreshPrizeDrawDaily();
        }
    }

    /**
     * 获取当天抽奖活动信息
     *
     * @return
     */
    private PrizeDrawRespVO getTodayMotoPrizeDraw(Long prizeDrawId) {
        RBucket<String> rBucket = redissonClient.getBucket(RedisKeyConstants.PRIZE_DRAW_KEY + prizeDrawId);
        String voJsonString = rBucket.get();
        if (StrUtil.isBlank(voJsonString)) {
            return null;
        }
        return JSONUtil.toBean(voJsonString, PrizeDrawRespVO.class);
    }

    /**
     * 刷新每日的抽奖活动信息
     */
    @Override
    public void refreshPrizeDrawDaily() {
        PrizeDrawRespVO originVo = prizeDrawMapper.getCurrentPrizeDrawActivity();
        if (originVo == null) {
            log.info("refreshPrizeDrawDaily-当前没有进行中的抽奖活动");
            return;
        }

        Long prizeDrawId = originVo.getPrizeDrawId();

        //原始奖项
        List<PrizeDrawAwardsRespVO> originAwards = prizeDrawAwardsService.getPrizeDrawAwardsListByPrizeDrawId(prizeDrawId);
        originVo.setAwards(originAwards);

        //获取原始的安慰奖
        PrizeDrawAwardsRespVO originComfortPrizeAwards = this.getComfortPrizeAwards(originAwards);

        PrizeDrawRespVO todayMotoPrizeDraw = this.getTodayMotoPrizeDraw(prizeDrawId);
        if (todayMotoPrizeDraw == null) {
            this.refreshTodayPrizeDraw(originVo);
            return;
        }

        //是否刷新今日中奖数量
        boolean isRefreshTodayWinNum = getIsRefreshTodayWinNum(todayMotoPrizeDraw);

        //保存每天的抽奖活动记录，用于排查分析
        this.savePrizeDrawAwardsRecord(todayMotoPrizeDraw, prizeDrawId);

        List<PrizeDrawAwardsRespVO> todayAwards = todayMotoPrizeDraw.getAwards();
        log.info("refreshPrizeDrawDaily-todayAwards:{}", JSONUtil.toJsonStr(todayAwards));
        //使用Stream API将List转换为Map，其中奖项的id属性作为键
        Map<Long, PrizeDrawAwardsRespVO> todayAwardsMap = todayAwards.stream().collect(Collectors.toMap(PrizeDrawAwardsRespVO::getPrizeDrawAwardsId, (motoPrizeDrawAwardsVo) -> motoPrizeDrawAwardsVo));

        //获取当天实时的安慰奖
        PrizeDrawAwardsRespVO todayComfortPrizeAwards = this.getComfortPrizeAwards(todayAwards);

        //设置总中奖数量和设置每日中奖数量为0
        originComfortPrizeAwards.setWinLotteryNum(todayComfortPrizeAwards.getWinLotteryNum());
        originComfortPrizeAwards.setTodayWinLotteryNum(0);

        originAwards.forEach(originAward -> {
            //奖项id
            Long prizeDrawAwardsId = originAward.getPrizeDrawAwardsId();
            PrizeDrawAwardsRespVO todayAward = todayAwardsMap.get(prizeDrawAwardsId);
            Integer winLotteryNum = 0;
            if (todayAward != null) {
                winLotteryNum = todayAward.getWinLotteryNum() == null ? 0 : todayAward.getWinLotteryNum();
            }
            Integer prizeNums = originAward.getPrizeNums() == null ? 0 : originAward.getPrizeNums();
            //设置总中奖数量和设置每日中奖数量为0
            originAward.setWinLotteryNum(winLotteryNum);
            log.info("isRefreshTodayWinNum:{}", isRefreshTodayWinNum);
            if (isRefreshTodayWinNum) {
                originAward.setTodayWinLotteryNum(0);
            } else {
                originAward.setTodayWinLotteryNum(todayAward.getTodayWinLotteryNum());
            }

            Integer todayWinLotteryNum = originAward.getTodayWinLotteryNum() == null ? 0 : originAward.getTodayWinLotteryNum();
            if (originAward.getDrawType().equals(1) && todayWinLotteryNum >= originAward.getTodayPrizeNums()) {
                BigDecimal prizeChance = originAward.getPrizeChance();
                BigDecimal comfortPrizeAwardsPrizeChance = originComfortPrizeAwards.getPrizeChance();
                //1-将当前 奖项的中奖率 放入到 安慰奖
                originComfortPrizeAwards.setPrizeChance(comfortPrizeAwardsPrizeChance.add(prizeChance));

                //2-再将当前奖项置为0
                originAward.setPrizeChance(BigDecimal.ZERO);
            }

            if (prizeNums != 0 && winLotteryNum >= prizeNums) {
                BigDecimal prizeChance = originAward.getPrizeChance();
                BigDecimal comfortPrizeAwardsPrizeChance = originComfortPrizeAwards.getPrizeChance();
                //1-将当前 奖项的中奖率 放入到 安慰奖
                originComfortPrizeAwards.setPrizeChance(comfortPrizeAwardsPrizeChance.add(prizeChance));

                //2-再将当前奖项置为0
                originAward.setPrizeChance(BigDecimal.ZERO);
            }
        });

        log.info("refreshPrizeDrawDaily-originVo-final:{}", JSONUtil.toJsonStr(originVo));
        originVo.setRefreshDate(DateUtil.today());
        refreshTodayPrizeDraw(originVo);
    }

    @Override
    public PrizeDrawRespVO getCurrentPrizeDraw() {
        PrizeDrawRespVO vo = prizeDrawMapper.getCurrentPrizeDrawActivity();
        if (vo != null) {
            List<PrizeDrawAwardsRespVO> awardsVoList = prizeDrawAwardsService.getPrizeDrawAwardsListByPrizeDrawId(vo.getPrizeDrawId());
            vo.setAwards(awardsVoList);

            //获取抽奖活动状态
            initPrizeDrawStatus(vo);

            //初始化进行中的抽奖活动到redis
            initRedisMotoPrizeDrawVo(vo);
            return vo;
        }

        vo = prizeDrawMapper.getRecentlyPrizeDraw();
        if (vo != null) {
            List<PrizeDrawAwardsRespVO> awardsVoList = prizeDrawAwardsService.getPrizeDrawAwardsListByPrizeDrawId(vo.getPrizeDrawId());
            vo.setAwards(awardsVoList);
            initPrizeDrawStatus(vo);
        }
        return vo;
    }

    /**
     * 获取抽奖活动状态
     *
     * @param vo
     */
    private void initPrizeDrawStatus(PrizeDrawRespVO vo) {
        Integer updown = vo.getUpdown();
        if (Integer.valueOf(1).equals(updown)) {
            //判断是否在活动时间范围内
            if (vo.getPrizeDrawBegin().compareTo(LocalDateTimeUtil.now()) > 0) {
                vo.setStatus(2);
                return;
            }

            if (vo.getPrizeDrawEnd().compareTo(LocalDateTimeUtil.now()) < 0) {
                vo.setStatus(4);
                return;
            }
            vo.setStatus(1);
        } else {
            vo.setStatus(3);
        }
    }

    /**
     * 初始化进行中的抽奖活动到redis
     *
     * @param vo
     */
    private void initRedisMotoPrizeDrawVo(PrizeDrawRespVO vo) {
        Long prizeDrawId = vo.getPrizeDrawId();
        //若抽奖活动已经存在redis，则无需初始化
        PrizeDrawRespVO todayMotoPrizeDraw = this.getTodayMotoPrizeDraw(prizeDrawId);
        if (todayMotoPrizeDraw != null) {
            return;
        }

        //若不存在，则重新刷新到redis
        this.refreshTodayPrizeDraw(vo);
    }

    @Override
    public PrizeDrawRespVO getInfoById(Long prizeDrawId) {
        PrizeDrawRespVO record = prizeDrawMapper.single(prizeDrawId);
        if (Objects.nonNull(record)) {
            if (ObjectUtil.isNotNull(record.getUpdater())) {
                MemberUserDO memberUserDO = memberUserMapper.selectById(record.getUpdater());
                if (Objects.nonNull(memberUserDO)) {
                    record.setUpdaterName(memberUserDO.getNickname());

                    List<PrizeDrawAwardsRespVO> awards = prizeDrawAwardsService.getPrizeDrawAwardsListByPrizeDrawId(record.getPrizeDrawId());
                    if (CollUtil.isNotEmpty(awards)) {
                        record.setAwards(awards);
                    }
                }
            }
        } else {
            throw exception(PRIZE_DRAW_NOT_EXISTS);
        }
        return record;
    }

    @Override
    public PrizeDrawAwardsRespVO doingPrizeDraw(PrizeDrawDoingReqVO reqVO) {
        Long prizeDrawId = reqVO.getPrizeDrawId();
        RLock lock = redissonClient.getLock(RedisKeyConstants.PRIZE_DRAW_LOCK_KEY + prizeDrawId);
        try {
            if (lock.tryLock(20, 2, TimeUnit.SECONDS)) {
                PrizeDrawServiceImpl prizeDrawService = SpringUtils.getBean(PrizeDrawServiceImpl.class);
                //开始进行抽奖,自己类里面调用，为了让事物生效
                return prizeDrawService.doingPrizeDrawStart(reqVO);
            } else {
                log.info("doingPrizeDraw 获取锁失败: {}", Thread.currentThread().getName());
                throw exception(PRIZE_DRAW_THE_EVENT_IS_TOO_HOT);
            }
        } catch (InterruptedException e) {
            log.error("doingPrizeDraw 获取锁失败: {}", Thread.currentThread().getName(), e);
            throw exception(PRIZE_DRAW_THE_EVENT_IS_TOO_HOT);
        } finally {
            lock.unlock();
        }
    }

    /**
     * 开始进行抽奖
     *
     * @param reqVO
     */
    @Transactional(rollbackFor = Exception.class)
    public PrizeDrawAwardsRespVO doingPrizeDrawStart(PrizeDrawDoingReqVO reqVO) {
        Long prizeDrawId = reqVO.getPrizeDrawId();
        Stopwatch stopwatch = Stopwatch.createStarted();
        log.info("doingPrizeDraw-MotoPrizeDrawDoingDto:{}", JSONUtil.toJsonStr(reqVO));
        Long userId = reqVO.getUserId();
        //获取当天的活动奖项信息
        PrizeDrawRespVO todayMotoPrizeDrawVo = this.getTodayMotoPrizeDraw(prizeDrawId);
        log.info("doingPrizeDraw-todayMotoPrizeDrawVo:{}", JSONUtil.toJsonStr(todayMotoPrizeDrawVo));
        //校验活动详情
        this.checkPrizeDrawVo(todayMotoPrizeDrawVo);

        //校验用户抽奖次数和中奖次数
        this.checkUserPrizeNum(userId, todayMotoPrizeDrawVo);

        //校验用户中奖次数
        boolean winPrizeNumFlag = this.checkUserWinPrizeNum(userId, todayMotoPrizeDrawVo);
        //开始抽奖
        PrizeDrawAwardsRespVO prizeDrawAwardsRespVO;
        if (!winPrizeNumFlag) {
            //如果用户达到了 当日中奖次数限制，则给 安慰奖
            prizeDrawAwardsRespVO = this.getComfortPrizeAwards(todayMotoPrizeDrawVo.getAwards());
        } else {
            prizeDrawAwardsRespVO = this.beginLottery(todayMotoPrizeDrawVo);

            if (Objects.nonNull(prizeDrawAwardsRespVO)) {
                if (Integer.valueOf(4).equals(prizeDrawAwardsRespVO.getAwardType())) {
//                    MotoPrizeDrawSettingShuYinBatchVo shuyinInfo = iMotoPrizeDrawSettingBatchService.shuyinInfo(prizeDrawAwardsRespVO.getProductId());
//                    if (Objects.nonNull(shuyinInfo)) {
//                        motoPrizeDrawAwardsVo.setPrizeName(shuyinInfo.getTitle());
//                    } else {
//                        // 数引实物删除了，不让中奖了
//                        motoPrizeDrawAwardsVo = this.getComfortPrizeAwards(todayMotoPrizeDrawVo.getAwards());
//                    }
                }

                // 如果是优惠券，立刻校验是否正常，不正常就给安慰奖
                if (Integer.valueOf(2).equals(prizeDrawAwardsRespVO.getAwardType())) {
//                    CouponPageListAdminRequest couponPageListAdminRequest = new CouponPageListAdminRequest();
//                    couponPageListAdminRequest.setCouponCode(motoPrizeDrawAwardsVo.getCouponCode());
//                    couponPageListAdminRequest.setStart(1);
//                    couponPageListAdminRequest.setLimit(1);
//
//                    CouponListAdminResponse couponListAdminResponse = remoteShuYinService.couponPageListAdmin(couponPageListAdminRequest, SecurityConstants.INNER);
//                    if (ObjectUtil.isNull(couponListAdminResponse) || ObjectUtil.notEqual(couponListAdminResponse.getCode(), "SUCCESS")) {
//                        motoPrizeDrawAwardsVo = this.getComfortPrizeAwards(todayMotoPrizeDrawVo.getAwards());
//                    } else {
//                        List<CouponListAdminPageResponse> rows = couponListAdminResponse.getRows();
//                        if (CollUtil.isEmpty(rows)) {
//                            motoPrizeDrawAwardsVo = this.getComfortPrizeAwards(todayMotoPrizeDrawVo.getAwards());
//                        } else {
//                            CouponListAdminPageResponse response = CollUtil.get(rows, 0);
//                            Integer totalNum = response.getTotalNum();
//
//                            if (totalNum <= 0) {
//                                motoPrizeDrawAwardsVo = this.getComfortPrizeAwards(todayMotoPrizeDrawVo.getAwards());
//                            }
//                        }
//                    }
                }
            }
        }

        //开始抽奖
        log.info("doingPrizeDraw-prizeDrawAwardsRespVO:{}", JSONUtil.toJsonStr(prizeDrawAwardsRespVO));
        if (prizeDrawAwardsRespVO == null) {
            return null;
        }

        /*
         * 中奖后续操作 减库存，增加中奖记录，增加每日中奖数量，刷新中奖概率等等
         */
        doAfterPrizeDrawAwards(prizeDrawAwardsRespVO, userId, todayMotoPrizeDrawVo);

        log.info("doingPrizeDraw 处理完成，耗时:{}", stopwatch.stop());
        return prizeDrawAwardsRespVO;
    }

    /**
     * 校验抽奖活动
     *
     * @param prizeDrawRespVO
     */
    private void checkPrizeDrawVo(PrizeDrawRespVO prizeDrawRespVO) {
        if (prizeDrawRespVO == null) {
            throw exception(PRIZE_DRAW_The_LOTTERY_ACTIVITY_DOES_NOT_EXIST_OR_HAS_NOT_STARTED_YET);
        }
        if (!Integer.valueOf(1).equals(prizeDrawRespVO.getUpdown())) {
            throw exception(PRIZE_DRAW_The_LOTTERY_ACTIVITY_HAS_NOT_YET_STARTED);
        }

        if (prizeDrawRespVO.getPrizeDrawBegin().compareTo(LocalDateTime.now()) > 0) {
            throw exception(PRIZE_DRAW_The_LOTTERY_ACTIVITY_HAS_NOT_STARTED_YET);
        }

        if (prizeDrawRespVO.getPrizeDrawEnd().compareTo(LocalDateTime.now()) < 0) {
            throw exception(PRIZE_DRAW_THE_THE_LOTTERY_ACTIVITY_HAS_ENDED);
        }
    }

    /**
     * 校验用户抽奖次数和中奖次数
     *
     * @param userId
     */
    private void checkUserPrizeNum(Long userId, PrizeDrawRespVO todayMotoPrizeDrawVo) {
        Integer number = prizeDrawNumberMemberService.getNumber(userId);
        if (number == null) {
            throw exception(PRIZE_DRAW_THE_USER_HAS_NOT_YET_OBTAINED_THE_NUMBER_OF_LUCKY_DRAWS);
        }

        if (number <= 0) {
            throw exception(PRIZE_DRAW_THE_USER_HAS_NOT_YET_OBTAINED_THE_NUMBER_OF_LUCKY_DRAWS);
        }
    }

    /**
     * 中奖后续操作 减库存，增加中奖记录，增加每日中奖数量，刷新中奖概率等等
     *
     * @param prizeDrawAwardsRespVO
     * @param userId
     * @param prizeDrawRespVO
     */
    private void doAfterPrizeDrawAwards(PrizeDrawAwardsRespVO prizeDrawAwardsRespVO, Long userId, PrizeDrawRespVO prizeDrawRespVO) {
        log.info("doAfterPrizeDrawAwards-prizeDrawAwardsRespVO:{},userId:{}，motoPrizeDrawVo：{}",
                JSONUtil.toJsonStr(prizeDrawAwardsRespVO), userId, JSONUtil.toJsonStr(prizeDrawRespVO));
        log.info("doAfterPrizeDrawAwards-addPrizeDrawLottery start:{}", userId);
        //新增中奖记录
        PrizeDrawLotteryDO prizeDrawLottery = this.addPrizeDrawLottery(prizeDrawAwardsRespVO, userId, prizeDrawRespVO);
        log.info("doAfterPrizeDrawAwards-addPrizeDrawLottery end:{}", userId);
        //中奖后返回中奖id
        prizeDrawAwardsRespVO.setPrizeDrawLotteryId(prizeDrawLottery.getPrizeDrawLotteryId());

        //先扣减redis的次数缓存，再异步扣减数据库的次数
        prizeDrawNumberMemberService.optionNumber(userId, -1);

        //异步扣减次数和操作虚拟记录(数据库)
        taskExecutor.execute(() -> doAfterPrizeDrawAwardsAsync(prizeDrawAwardsRespVO, prizeDrawLottery, userId, prizeDrawRespVO));

        //非安慰奖时，才进行 刷新总中奖数量和今日中奖数量，并刷新用户每日中奖次数
        if (!Integer.valueOf(2).equals(prizeDrawAwardsRespVO.getDrawType())) {
            //刷新 奖项总中奖数量和今日中奖量
            this.refreshWinLotteryNumAndToday(prizeDrawAwardsRespVO, prizeDrawRespVO);
            log.info("doAfterPrizeDrawAwards-refreshWinLotteryNumAndToday end:{}", userId);

            log.info("doAfterPrizeDrawAwards-refreshUserTodayPrizeNum start:{}", userId);
            //刷新用户每日中奖次数
            this.refreshUserTodayPrizeNum(userId, prizeDrawRespVO);
            log.info("doAfterPrizeDrawAwards-refreshUserTodayPrizeNum end:{}", userId);
        }
    }

    public void doAfterPrizeDrawAwardsAsync(PrizeDrawAwardsRespVO prizeDrawAwardsRespVO,
                                            PrizeDrawLotteryDO prizeDrawLottery,
                                            Long userId,
                                            PrizeDrawRespVO prizeDrawRespVO) {
        log.info("doAfterPrizeDrawAwards-reduceNumber start:{}", userId);
        //扣减用户抽奖次数
        prizeDrawNumberMemberService.reduceNumber(
                PrizeDrawReduceNumberRespVO.builder().
                        userId(userId).
                        settingType(6).
                        //放中奖记录的id
                                businessId(String.valueOf(prizeDrawLottery.getPrizeDrawLotteryId())).
                        build());
        log.info("doAfterPrizeDrawAwards-reduceNumber end:{}", userId);

        log.info("doAfterPrizeDrawAwards-addVirtualRecord start:{}", userId);
        //新增虚拟物兑奖记录
        this.addVirtualRecord(prizeDrawAwardsRespVO, prizeDrawLottery.getPrizeDrawLotteryId(), userId);
        log.info("doAfterPrizeDrawAwards-addVirtualRecord end:{}", userId);

        log.info("doAfterPrizeDrawAwards-refreshWinLotteryNumAndToday start:{}", userId);
    }

    /**
     * 新增虚拟物兑奖记录
     *
     * @param prizeDrawAwardsRespVO
     * @param prizeDrawLotteryId
     * @param userId
     */
    private void addVirtualRecord(PrizeDrawAwardsRespVO prizeDrawAwardsRespVO, Long prizeDrawLotteryId, Long userId) {
        Integer awardType = prizeDrawAwardsRespVO.getAwardType();
        //若不是 积分或优惠券，则不进行 直接兑换
        if (!PrizeDrawAwardTypeEnum.POINT.getType().equals(awardType) && !PrizeDrawAwardTypeEnum.COUPON.getType().equals(awardType)) {
            return;
        }

        if (PrizeDrawAwardTypeEnum.POINT.getType().equals(awardType)) {
            log.info("【幸运大转盘】中奖，积分+{}", prizeDrawAwardsRespVO.getPoint());
            //TODO 增加积分明细，并刷新用户积分

        }

        if (PrizeDrawAwardTypeEnum.COUPON.getType().equals(awardType)) {
            log.info("【幸运大转盘】中奖，优惠券+{}", prizeDrawAwardsRespVO.getPrizeName());
            //TODO 增加优惠券明细，并刷新用户优惠券

        }
        //更新 中奖记录 领取信息
        PrizeDrawLotteryDO prizeDrawLottery = new PrizeDrawLotteryDO();
        prizeDrawLottery.setPrizeDrawLotteryId(prizeDrawLotteryId);
        prizeDrawLottery.setStatus(PrizeDrawLotteryStatusEnum.GRANT_VIRTUAL.getType());
        prizeDrawLottery.setIssueTime(LocalDateTime.now());
        prizeDrawLotteryMapper.updateById(prizeDrawLottery);
    }

    /**
     * 刷新总中奖数量和今日中奖数量
     *
     * @param prizeDrawAwardsRespVO
     * @param prizeDrawRespVO
     */
    private void refreshWinLotteryNumAndToday(PrizeDrawAwardsRespVO prizeDrawAwardsRespVO, PrizeDrawRespVO prizeDrawRespVO) {
        List<PrizeDrawAwardsRespVO> awards = prizeDrawRespVO.getAwards();

        //获取安慰奖
        PrizeDrawAwardsRespVO comfortPrizeAwards = this.getComfortPrizeAwards(awards);

        awards.forEach(awardsVo -> {
            if (awardsVo.getPrizeDrawAwardsId().equals(prizeDrawAwardsRespVO.getPrizeDrawAwardsId())) {
                Integer prizeNums = awardsVo.getPrizeNums();
                Integer todayPrizeNums = awardsVo.getTodayPrizeNums();

                int winLotteryNum = awardsVo.getWinLotteryNum() == null ? 0 : awardsVo.getWinLotteryNum();
                winLotteryNum = winLotteryNum + 1;
                awardsVo.setWinLotteryNum(winLotteryNum);

                int todayWinLotteryNum = awardsVo.getTodayWinLotteryNum() == null ? 0 : awardsVo.getTodayWinLotteryNum();
                todayWinLotteryNum = todayWinLotteryNum + 1;
                awardsVo.setTodayWinLotteryNum(todayWinLotteryNum);

                if (prizeNums.equals(winLotteryNum) || todayPrizeNums.equals(todayWinLotteryNum)) {
                    //如果 总数量已中完或当日中奖数量已中完，则将该奖项的中奖率置为0，将其中奖率放入安慰奖
                    BigDecimal prizeChance = awardsVo.getPrizeChance();
                    awardsVo.setPrizeChance(new BigDecimal(0));
                    if (comfortPrizeAwards != null) {
                        BigDecimal comfortPrizeChance = comfortPrizeAwards.getPrizeChance();
                        comfortPrizeAwards.setPrizeChance(prizeChance.add(comfortPrizeChance));
                    }
                }
            }
        });

        //刷新每日抽奖活动信息
        refreshTodayPrizeDraw(prizeDrawRespVO);
    }

    /**
     * 刷新用户每日中奖次数
     *
     * @param userId
     * @param prizeDrawRespVO
     */
    private void refreshUserTodayPrizeNum(Long userId, PrizeDrawRespVO prizeDrawRespVO) {
        String today = DateUtil.today();
        String redisKey = RedisKeyConstants.PRIZE_DRAW_NUM_MEMBER_KEY + prizeDrawRespVO.getPrizeDrawId() + ":" + userId + ":" + today;

        Integer prizeDrawNumber = this.getUserTodayPrizeNum(userId, prizeDrawRespVO);
        redissonClient.getBucket(redisKey).set(prizeDrawNumber + 1);
    }

    /**
     * 新增中奖记录
     *
     * @param prizeDrawAwardsRespVO
     * @param userId
     * @param prizeDrawRespVO
     */
    private PrizeDrawLotteryDO addPrizeDrawLottery(PrizeDrawAwardsRespVO prizeDrawAwardsRespVO, Long userId, PrizeDrawRespVO prizeDrawRespVO) {
        PrizeDrawLotteryDO prizeDrawLottery = new PrizeDrawLotteryDO();
        //获取是否中奖和状态
        initIsLotteryAndStatus(prizeDrawLottery, prizeDrawAwardsRespVO);
        prizeDrawLottery.setPrizeDrawId(prizeDrawRespVO.getPrizeDrawId());
        prizeDrawLottery.setUserId(userId);
        prizeDrawLottery.setPrizeDrawAwardsId(prizeDrawAwardsRespVO.getPrizeDrawAwardsId());
        prizeDrawLottery.setLotteryTime(LocalDateTimeUtil.now());

        LocalDateTime expiredTime = this.calExpiredTime(prizeDrawAwardsRespVO, prizeDrawRespVO);
        //计算 兑换过期时间
        prizeDrawLottery.setExpiredTime(expiredTime);

        //给奖项也返回过期时间，前端需要使用
        prizeDrawAwardsRespVO.setExpiredTime(expiredTime);

        prizeDrawLotteryMapper.insert(prizeDrawLottery);

        if (expiredTime != null) {
            Integer kindExchangeNums = prizeDrawRespVO.getKindExchangeNums();
            //奖中奖过期放入redis，进行过期监听
            String redisKey = RedisKeyConstants.PRIZE_DRAW_LOTTERY_EXPIREDTIME_KEY + prizeDrawLottery.getPrizeDrawLotteryId();
            redissonClient.getBucket(redisKey).set(prizeDrawLottery, kindExchangeNums, TimeUnit.DAYS);
        }
        return prizeDrawLottery;
    }

    private void initIsLotteryAndStatus(PrizeDrawLotteryDO prizeDrawLottery, PrizeDrawAwardsRespVO prizeDrawAwardsRespVO) {
        Integer awardType = prizeDrawAwardsRespVO.getAwardType();

        if (PrizeDrawAwardTypeEnum.NOTHING.getType().equals(awardType)) {
            prizeDrawLottery.setIsLottery(0);
            //如果是 未中奖，先将中奖记录设置为 已发放
            prizeDrawLottery.setStatus(PrizeDrawLotteryStatusEnum.GRANT_VIRTUAL.getType());
            return;
        }

        if (PrizeDrawAwardTypeEnum.OTHER_VIRTUAL.getType().equals(awardType)) {
            prizeDrawLottery.setIsLottery(1);
            //如果是其他虚拟， 先将中奖记录设置为 待发货
            prizeDrawLottery.setStatus(PrizeDrawLotteryStatusEnum.WAIT_DELIVERY.getType());
            return;
        }

        if (PrizeDrawAwardTypeEnum.COUPON.getType().equals(awardType) || PrizeDrawAwardTypeEnum.POINT.getType().equals(awardType)) {
            prizeDrawLottery.setIsLottery(1);
            //如果是龙豆或优惠券， 先将中奖记录设置为 已发货
            prizeDrawLottery.setStatus(PrizeDrawLotteryStatusEnum.GRANT_VIRTUAL.getType());
            return;
        }

        prizeDrawLottery.setIsLottery(1);
        //先将中奖记录设置为 待兑换
        prizeDrawLottery.setStatus(PrizeDrawLotteryStatusEnum.WAIT_GRANT.getType());
    }

    /**
     * 计算中奖过期时间
     *
     * @param prizeDrawAwardsRespVO
     * @param prizeDrawRespVO
     * @return
     */
    private LocalDateTime calExpiredTime(PrizeDrawAwardsRespVO prizeDrawAwardsRespVO, PrizeDrawRespVO prizeDrawRespVO) {
        Integer kindExchangeType = prizeDrawRespVO.getKindExchangeType();
        if (Integer.valueOf(1).equals(kindExchangeType)) {
            return null;
        }

        //实物时，才需要计算 兑换过期时间
        Integer awardType = prizeDrawAwardsRespVO.getAwardType();
        if (PrizeDrawAwardTypeEnum.POINT.getType().equals(awardType) || PrizeDrawAwardTypeEnum.COUPON.getType().equals(awardType) || PrizeDrawAwardTypeEnum.OTHER_VIRTUAL.getType().equals(awardType) || PrizeDrawAwardTypeEnum.NOTHING.getType().equals(awardType)) {
            return null;
        }

        return LocalDateTimeUtil.offset(LocalDateTimeUtil.now(), prizeDrawRespVO.getKindExchangeNums(), ChronoUnit.DAYS);
    }

    /**
     * 校验用户中奖次数
     *
     * @param userId
     * @param todayMotoPrizeDrawVo
     */
    private boolean checkUserWinPrizeNum(Long userId, PrizeDrawRespVO todayMotoPrizeDrawVo) {
        Integer todayTotalType = todayMotoPrizeDrawVo.getTodayTotalType();
        if (Integer.valueOf(1).equals(todayTotalType)) {
            return true;
        }

        Integer todayPrizeNums = todayMotoPrizeDrawVo.getTodayTotalNums();
        Integer userTodayPrizeNum = this.getUserTodayPrizeNum(userId, todayMotoPrizeDrawVo);
        return userTodayPrizeNum < todayPrizeNums;
    }

    /**
     * 获取用户每日中奖次数
     *
     * @param userId
     * @param prizeDrawVo
     * @return
     */
    private Integer getUserTodayPrizeNum(Long userId, PrizeDrawRespVO prizeDrawVo) {
        String today = DateUtil.today();
        String redisKey = RedisKeyConstants.PRIZE_DRAW_NUM_MEMBER_KEY + prizeDrawVo.getPrizeDrawId() + ":" + userId + ":" + today;

        RBucket<Integer> rBucket =redissonClient.getBucket(redisKey);
        Integer prizeDrawNumber = rBucket.get();
        if (prizeDrawNumber == null) {
            prizeDrawNumber = 0;
        }
        log.info("getUserTodayPrizeNum-userId:{},prizeDrawNumber:{}", userId, prizeDrawNumber);
        return prizeDrawNumber;
    }

    /**
     * 刷新当天的 进行中抽奖活动
     *
     * @param prizeDrawRespVO
     */
    private void refreshTodayPrizeDraw(PrizeDrawRespVO prizeDrawRespVO) {
        log.info("refreshTodayPrizeDraw-prizeDrawRespVO:{}", JSONUtil.toJsonStr(prizeDrawRespVO));
        //重新计算 中奖起始数
        this.calAwardProbability(prizeDrawRespVO);
        //刷新 每日抽奖活动信息
        RBucket<String> rBucket = redissonClient.getBucket(RedisKeyConstants.PRIZE_DRAW_KEY + prizeDrawRespVO.getPrizeDrawId());
        rBucket.set(JSONUtil.toJsonStr(prizeDrawRespVO));
    }

    /**
     * 获取安慰奖项
     *
     * @param awards
     * @return
     */
    private PrizeDrawAwardsRespVO getComfortPrizeAwards(List<PrizeDrawAwardsRespVO> awards) {
        for (PrizeDrawAwardsRespVO awardsVo : awards) {
            if (awardsVo.getDrawType().equals(Integer.valueOf(2))) {
                return awardsVo;
            }
        }
        return null;
    }

    /**
     * 是否刷新今日中奖数量
     *
     * @param todayMotoPrizeDraw
     * @return
     */
    private boolean getIsRefreshTodayWinNum(PrizeDrawRespVO todayMotoPrizeDraw) {
        String refreshDate = todayMotoPrizeDraw.getRefreshDate();
        if (StrUtil.isBlank(refreshDate)) {
            return false;
        }
        String todayDate = DateUtil.today();
        if (refreshDate.equals(todayDate)) {
            return false;
        }
        return true;
    }

    /**
     * 保存每天的抽奖活动记录，用于排查分析
     *
     * @param todayMotoPrizeDraw
     * @param prizeDrawId
     */
    private void savePrizeDrawAwardsRecord(PrizeDrawRespVO todayMotoPrizeDraw, Long prizeDrawId) {
        try {
            PrizeDrawAwardsRecordDO prizeDrawAwardsRecord = new PrizeDrawAwardsRecordDO();
            prizeDrawAwardsRecord.setPrizeDrawId(prizeDrawId);
            prizeDrawAwardsRecord.setAwardContent(JSONUtil.toJsonStr(todayMotoPrizeDraw));
            prizeDrawAwardsRecord.setRecordDate(LocalDateTimeUtil.now());
            //保存每天的抽奖活动记录，用于排查分析
            prizeDrawAwardsRecordMapper.insert(prizeDrawAwardsRecord);
        } catch (Exception e) {
            log.error("savePrizeDrawAwardsRecord-error:{}", e.getMessage());
        }

    }

    public void calAwardProbability(PrizeDrawRespVO prizeDrawRespVO) {
        List<PrizeDrawAwardsRespVO> awards = prizeDrawRespVO.getAwards();

        Integer codeScope = 1;
        for (PrizeDrawAwardsRespVO item : awards) {
            Integer nowScope = 1;
            BigDecimal prizeChance = item.getPrizeChance();

            Double awardProbability = prizeChance.divide(new BigDecimal(100)).setScale(2, BigDecimal.ROUND_CEILING).doubleValue();
            while (true) {
                Double test = awardProbability * nowScope;
                // 概率的精确度，调整到小数点后10位，概率太小等于不中奖，跳出
                if (test < 0.0000000001) {
                    break;
                }
                if ((test >= 1L && (test - test.longValue()) < 0.0001D) || nowScope >= MAXSOPE) {
                    if (nowScope > codeScope) {
                        // 设置中奖范围
                        codeScope = nowScope;
                    }
                    break;
                } else {
                    // 中奖数字范围以10倍进行增长
                    nowScope = nowScope * 10;
                }
            }
        }
        Integer winningStartCode = 0;
        Integer winningEndCode = winningStartCode;

        for (PrizeDrawAwardsRespVO item : awards) {
            Integer codeNum = (int) (item.getPrizeChance().doubleValue() * codeScope); // 获得其四舍五入的整数值
            // 无人中奖时，将中奖的起始范围设置在随机数的范围之外
            if (codeNum == 0) {
                item.setAwardStartCode(-codeScope - 1);
                item.setAwardEndCode(-codeScope - 1);
            } else {
                item.setAwardStartCode(winningEndCode);
                item.setAwardEndCode(winningEndCode + codeNum - 1);
                winningEndCode = winningEndCode + codeNum;
            }
        }
        // 设置用户的中奖随机码信息
        prizeDrawRespVO.setWinningStartCode(winningStartCode);
        prizeDrawRespVO.setWinningEndCode(winningEndCode);
        prizeDrawRespVO.setCodeScope(codeScope * 100);
    }

    public PrizeDrawAwardsRespVO beginLottery(PrizeDrawRespVO prizeDrawRespVO) {
        List<PrizeDrawAwardsRespVO> awards = prizeDrawRespVO.getAwards();
        // 确定活动是否有效,如果活动无效则，直接抽奖失败
        Integer randomCode = RandomUtil.randomInt(prizeDrawRespVO.getCodeScope());
        if (randomCode >= prizeDrawRespVO.getWinningStartCode() && randomCode <= prizeDrawRespVO.getWinningEndCode()) {
            for (PrizeDrawAwardsRespVO item : awards) {
                if (randomCode >= item.getAwardStartCode() && randomCode <= item.getAwardEndCode()) {
                    item.setAwardCode(randomCode);
                    return item;
                }
            }
        }
        return null;
    }

    private void validatePrizeDrawExists(List<Long> ids) {
        List<PrizeDrawDO> list = prizeDrawMapper.selectByIds(ids);
        if (CollUtil.isEmpty(list) || list.size() != ids.size()) {
            throw exception(PRIZE_DRAW_NOT_EXISTS);
        }
    }

    private void validatePrizeDrawExists(Long id) {
        if (prizeDrawMapper.selectById(id) == null) {
            throw exception(PRIZE_DRAW_NOT_EXISTS);
        }
    }

    @Override
    public PrizeDrawRespVO getPrizeDraw(Long id) {
        PrizeDrawRespVO record = prizeDrawMapper.single(id);
        if (Objects.nonNull(record)) {
            MemberUserDO memberUserDO = memberUserMapper.selectById(id);

            if (ObjectUtil.isNotNull(memberUserDO)) {
                record.setUpdaterName(memberUserDO.getNickname());

                List<PrizeDrawAwardsRespVO> prizeDrawAwardsRespVOS = prizeDrawAwardsService.getPrizeDrawAwardsListByPrizeDrawId(id);

                if (CollUtil.isNotEmpty(prizeDrawAwardsRespVOS)) {
                    record.setAwards(prizeDrawAwardsRespVOS);
                }
            }
        } else {
            throw exception(PRIZE_DRAW_NOT_EXISTS);
        }

        return record;
    }

    @Override
    public PageResult<PrizeDrawRespVO> getPrizeDrawPage(PrizeDrawPageReqVO pageReqVO) {
        PageResult<PrizeDrawRespVO> pageResult = prizeDrawMapper.getPrizeDrawPage(pageReqVO);
        List<PrizeDrawRespVO> records = pageResult.getList();

        if (CollUtil.isNotEmpty(records)) {
            List<Long> updaterIds = records.stream().map(PrizeDrawRespVO::getUpdater).distinct().collect(Collectors.toList());

            List<MemberUserDO> memberUserDOS = memberUserMapper.selectByIds(updaterIds);

            if (CollUtil.isNotEmpty(memberUserDOS)) {
                Map<Long, MemberUserDO> memberUserMap = memberUserDOS.stream().collect(Collectors.toMap(MemberUserDO::getId, a -> a));
                for (PrizeDrawRespVO drawRespVO : records) {
                    MemberUserDO userDO = memberUserMap.get(drawRespVO.getUpdater());
                    if (ObjectUtil.isNotNull(userDO)) {
                        drawRespVO.setUpdaterName(userDO.getNickname());
                    }
                }
            }
        }
        return pageResult;
    }

    @Override
    public void chancePreserve(PrizeDrawChanceReqVO chanceReqVO) {
        List<PrizeDrawAwardsChanceReqVO> awards = chanceReqVO.getAwards();
        if (CollUtil.isNotEmpty(awards)) {
            PrizeDrawAwardsChanceReqVO comfort = null;
            BigDecimal hundred = new BigDecimal("100");

            Map<Long, PrizeDrawStoreRespVo> mpdam = MapUtil.newHashMap();
            List<PrizeDrawStoreRespVo> rawards = getStore(chanceReqVO.getPrizeDrawId());
            if (CollUtil.isNotEmpty(rawards)) {
                mpdam = rawards.stream().collect(Collectors.toMap(PrizeDrawStoreRespVo::getPrizeDrawAwardsId, y -> y));
            }

            for (PrizeDrawAwardsChanceReqVO award : awards) {
                PrizeDrawAwardsDO ori = prizeDrawAwardsMapper.selectById(award.getPrizeDrawAwardsId());

                // 这里要验证概率，并计算出安慰奖的概率
                if (Integer.valueOf(1).equals(ori.getDrawType())) {
                    PrizeDrawStoreRespVo rnew = mpdam.get(award.getPrizeDrawAwardsId());

                    if (rnew.getRemainings() <= 0) {
                        if (award.getPrizeChance().compareTo(BigDecimal.ZERO) > 0) {
                            throw exception(PRIZE_DRAW_THE_PRIZES_HAVE_BEEN_DISTRIBUTED, ori.getPrizeName());
                        }
                    }

                    hundred = NumberUtil.sub(hundred, award.getPrizeChance());
                    if (hundred.compareTo(BigDecimal.ZERO) < 0) {
                        throw exception(PRIZE_DRAW_PROBABILITY_SETTING_ERROR);
                    }

                    prizeDrawAwardsMapper.update(new LambdaUpdateWrapper<PrizeDrawAwardsDO>().eq(PrizeDrawAwardsDO::getPrizeDrawAwardsId, award.getPrizeDrawAwardsId())
                            .set(PrizeDrawAwardsDO::getPrizeChance, award.getPrizeChance()));
                } else {
                    comfort = BeanUtil.copyProperties(award, PrizeDrawAwardsChanceReqVO.class);
                }
            }

            // 单独处理安慰奖
            if (Objects.nonNull(comfort)) {
                prizeDrawAwardsMapper.update(new LambdaUpdateWrapper<PrizeDrawAwardsDO>().eq(PrizeDrawAwardsDO::getPrizeDrawAwardsId, comfort.getPrizeDrawAwardsId())
                        .set(PrizeDrawAwardsDO::getPrizeChance, hundred));
            }
        }

        //修改完概率后，需要重新刷新redis信息
        refreshRedisMotoPrizeDrawVoByIds(ListUtil.of(chanceReqVO.getPrizeDrawId()));
    }

    @Override
    public void updown(PrizeDrawUpdownReqVO updownReqVO) {
        Integer updown = updownReqVO.getUpdown();
        List<Long> prizeDrawIds = updownReqVO.getPrizeDrawIds();

        for (Long prizeDrawId : prizeDrawIds) {
            if (Integer.valueOf(1).equals(updown)) {
                PrizeDrawDO draw = prizeDrawMapper.selectById(prizeDrawId);
                PrizeDrawRespVO intersection = prizeDrawMapper.intersectionVo(draw);
                if (Objects.nonNull(intersection)) {
                    throw exception(PRIZE_DRAW_THERE_IS_AN_INTERSECTION_IN_TIME_THAT_CANNOT_BE_ENABLED, draw.getPrizeDrawName(), intersection.getPrizeDrawName());
                }
            }
        }
        prizeDrawMapper.update(new LambdaUpdateWrapper<PrizeDrawDO>().in(PrizeDrawDO::getPrizeDrawId, prizeDrawIds).set(PrizeDrawDO::getUpdown, updown));

        //修改完状态后，需要重新刷新redis信息
        for (Long prizeDrawId : prizeDrawIds) {
            PrizeDrawRespVO todayMotoPrizeDraw = this.getTodayMotoPrizeDraw(prizeDrawId);
            //当活动存在redis时，才进行更新
            if (todayMotoPrizeDraw == null) {
                continue;
            }
            todayMotoPrizeDraw.setUpdown(updown);
            //将最新的 原始数据和当天的 实时活动数据刷新
            refreshTodayPrizeDraw(todayMotoPrizeDraw);
        }
    }

    @Override
    public void delete(PrizeDrawDeleteReqVO deleteReqVO) {
        prizeDrawMapper.deleteById(deleteReqVO.getPrizeDrawId());
    }

    @Override
    public List<PrizeDrawStoreRespVo> getStore(Long prizeDrawId) {
        List<PrizeDrawStoreRespVo> pds = prizeDrawMapper.getStore(prizeDrawId);

        RBucket<String> rBucket = redissonClient.getBucket(RedisKeyConstants.PRIZE_DRAW_KEY + prizeDrawId);
        String mpds = rBucket.get();
        if (StrUtil.isNotBlank(mpds)) {
            PrizeDrawRespVO mpd = JSONUtil.toBean(mpds, PrizeDrawRespVO.class);
            List<PrizeDrawAwardsRespVO> awards = mpd.getAwards();
            if (CollUtil.isNotEmpty(awards)) {
                for (PrizeDrawStoreRespVo pd : pds) {
                    for (PrizeDrawAwardsRespVO award : awards) {
                        if (pd.getPrizeDrawAwardsId().equals(award.getPrizeDrawAwardsId())) {
                            pd.setWinnings(award.getWinLotteryNum() == null ? 0 : award.getWinLotteryNum());
                            pd.setRemainings(pd.getTotals() - pd.getWinnings());
                        }
                    }
                }
            }
        } else {
            for (PrizeDrawStoreRespVo pd : pds) {
                pd.setWinnings(0);
                pd.setRemainings(pd.getTotals());
            }
        }

        return pds;
    }

}